package hawk.example.jdk.io;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.UncheckedIOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.ToLongFunction;
import java.util.zip.CRC32;

public class MemoryMapTest {

    public static void main(String[] args) throws IOException {
        Path filename = Path.of("C:\\mydata\\temp\\spring-boot-reference.pdf");
        crcFile("Input Stream", filename, MemoryMapTest::checksumInputStream);
        crcFile("Buffered Input Stream", filename, MemoryMapTest::checksumBufferedInputStream);
        crcFile("Random Access File", filename, MemoryMapTest::checksumRandomAccessFile);
        crcFile("Buffered Input Stream", filename, MemoryMapTest::checksumMappedFile);
    }

    public static void crcFile(String title, Path filename, ToLongFunction<Path> crc) {
        System.out.println(title + ":");
        long start = System.currentTimeMillis();
        long crcValue = crc.applyAsLong(filename);
        long end = System.currentTimeMillis();
        System.out.println(Long.toHexString(crcValue));
        System.out.println((end - start) + " milliseconds");
    }

    public static long checksumInputStream(Path filename)  {
        try (InputStream in = Files.newInputStream(filename)) {
            var crc = new CRC32();
            boolean done = false;
            while (!done) {
                int c = in.read();
                if (c == -1) {
                    done = true;
                } else {
                    crc.update(c);
                }
            }
            return crc.getValue();
        }catch(IOException ex) {
        	throw new UncheckedIOException(ex);
        }
    }

    public static long checksumBufferedInputStream(Path filename)  {
        try (var in = new BufferedInputStream(Files.newInputStream(filename))) {
            var crc = new CRC32();
            boolean done = false;
            while (!done) {
                int c = in.read();
                if (c == -1) {
                    done = true;
                } else {
                    crc.update(c);
                }
            }
            return crc.getValue();
        }catch(IOException ex) {
        	throw new UncheckedIOException(ex);
        }
    }

    public static long checksumRandomAccessFile(Path filename)  {
        try (var file = new RandomAccessFile(filename.toFile(), "r")) {
            long length = file.length();
            var crc = new CRC32();
            for (long p = 0; p < length; p++) {
                int c = file.readByte();
                crc.update(c);
            }
            return crc.getValue();
        }catch(IOException ex) {
        	throw new UncheckedIOException(ex);
        }
    }

    public static long checksumMappedFile(Path filename)  {
        try (FileChannel channel = FileChannel.open(filename)) {
            var crc = new CRC32();
            long length = channel.size();
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
            for (int i = 0; i < length; i++) {
                int c = buffer.get(i);
                crc.update(c);
            }
            return crc.getValue();
        }catch(IOException ex) {
        	throw new UncheckedIOException(ex);
        }
    }
}
